using System;
using System.Text;
using System.Globalization;


namespace Waymex.Gps.Nmea
{

    /// <summary>
    /// PMGNRTE NMEA propriertry sentence class.
    /// </summary>
    public class SentencePmgnRte : Sentence
    {
        private const string SENTENCE_TYPE = "PMGNRTE";

        private int m_numberOfMessages = 0;
        private int m_messageNumber = 0;
        private string m_messageType = ""; //"c" or "m"
        private int m_routeNumber = 0;
        private string m_routeName = "";
        private string m_routeMessage = "";
        private PmgnRteRoutePointCollection m_routePoints = new PmgnRteRoutePointCollection();
        private IconStatus m_iconStatus = IconStatus.Unknown;

        //enumeration to determine if icons exist within the routepoints
        /// <summary>
        /// Enumerator to identify the Icon Status.
        /// </summary>
        public enum IconStatus
        {
            /// <summary>
            /// Icon status unknown.
            /// </summary>
            Unknown,
            /// <summary>
            /// Icons used in this sentence.
            /// </summary>
            IconsUsed,
            /// <summary>
            /// Icons not used in this sentence.
            /// </summary>
            IconsNotUsed
        }
        /// <summary>
        /// Constructor for the class.
        /// </summary>
        public SentencePmgnRte()
            : base()
        {
        }
        /// <summary>
        /// Constructor for the class.
        /// </summary>
        /// <param name="sentence">NMEA sentence.</param>
        public SentencePmgnRte(string sentence)
            : base(sentence)
        {
            try
            {
                //check the type to ensure that we can continue
                if (this.TypeId.ToUpper(CultureInfo.InvariantCulture) == SENTENCE_TYPE)
                {
                    Populate(sentence);
                }
                else
                {
                    throw new InvalidSentence(C_MSG_001);
                }
            }
            catch (Exception e)
            {
                throw e;
            }
        }
        private void Populate(string p_strSentence)
        {
            if (SentenceItemExists(1)) m_numberOfMessages = Convert.ToInt32(FormatNumber(m_astrSentence[1]), CultureInfo.InvariantCulture);
            if (SentenceItemExists(2)) m_messageNumber = Convert.ToInt32(FormatNumber(m_astrSentence[2]), CultureInfo.InvariantCulture);
            if (SentenceItemExists(3)) m_messageType = m_astrSentence[3];
            if (SentenceItemExists(4)) m_routeNumber = Convert.ToInt32(FormatNumber(m_astrSentence[4]), CultureInfo.InvariantCulture);

            //assume that this is the route name, 
            //however it could be the first waypoint name depending upon actual device
            if (SentenceItemExists(5)) m_routeName = m_astrSentence[5];

            switch (m_messageType.ToUpper(CultureInfo.InvariantCulture))
            {
                case "C":	//Complete
                    //add the points
                    int numberOfPoints = m_astrSentence.Length - 6;
                    if (numberOfPoints > 0)
                    {
                        //m_routePoints = new string[numberOfPoints];
                        for (int f = 0; f < numberOfPoints; f++)
                        {
                            //this could include waypoint icons and waypoint names
                            //we cannot determine if icons are used until the 
                            //routename issues have been resolved
                            //the routename cannot be resolved until all route messages have
                            //been retrieved
                            PmgnRteRoutePoint point = new PmgnRteRoutePoint();
                            point.Name = m_astrSentence[6 + f];
                            m_routePoints.Add(point);
                        }
                        Icons = IconStatus.Unknown;
                    }
                    else
                    {
                        m_routePoints = null;
                    }
                    break;
                case "M":  //Message
                    if (SentenceItemExists(6)) m_routeMessage = m_astrSentence[6];

                    break;
                default:
                    // some other undocumented message type
                    break;
            }

        }
        /// <summary>
        /// Gets/Sets the number of messages.
        /// </summary>
        public int NumberOfMessages
        {
            get
            {
                return m_numberOfMessages;
            }
            set
            {
                m_numberOfMessages = value;
            }
        }
        /// <summary>
        /// Gets/Sets the current message number.
        /// </summary>
        public int MessageNumber
        {
            get
            {
                return m_messageNumber;
            }
            set
            {
                m_messageNumber = value;
            }
        }
        /// <summary>
        /// Gets/Sets the current message type
        /// </summary>
        public string MessageType
        {
            get
            {
                return m_messageType;
            }
            set
            {
                m_messageType = value;
            }
        }
        /// <summary>
        /// Gets/Sets the Route Number.
        /// </summary>
        public int RouteNumber
        {
            get
            {
                return m_routeNumber;
            }
            set
            {
                m_routeNumber = value;
            }
        }
        /// <summary>
        /// Gets/Sets the Route Name.
        /// </summary>
        public string RouteName
        {
            get
            {
                return m_routeName;
            }
            set
            {
                m_routeName = value;
            }
        }
        /// <summary>
        /// Gets/Sets the Route Message.
        /// </summary>
        public string RouteMessage
        {
            get
            {
                return m_routeMessage;
            }
            set
            {
                m_routeMessage = value;
            }
        }
        /// <summary>
        /// Gets/Sets the Rout Points.
        /// </summary>
        public PmgnRteRoutePointCollection RoutePoints
        {
            get
            {
                return m_routePoints;
            }
            set
            {
                m_routePoints = value;
            }
        }
        /// <summary>
        /// Gets/Sets the Icon Status.
        /// </summary>
        public IconStatus Icons
        {
            get
            {
                return m_iconStatus;
            }
            set
            {
                m_iconStatus = value;
            }
        }
        /// <summary>
        /// Returns the sentence as a correctlt formatted NMEA sentence with checksum.
        /// </summary>
        /// <returns>NMEA sentence.</returns>
        public override string ToString()
        {
            try
            {
                //need to see if sentence was passed in constructor
                if (m_strSentence.Length == 0)
                {

                    //not passed in so create from properties
                    StringBuilder sbSentence = new StringBuilder();
                    StringBuilder sbRoutePoints = new StringBuilder();

                    sbSentence.Append("$");
                    sbSentence.Append(SENTENCE_TYPE);
                    sbSentence.Append(",");
                    sbSentence.Append(this.NumberOfMessages.ToString(CultureInfo.InvariantCulture));
                    sbSentence.Append(",");
                    sbSentence.Append(this.MessageNumber.ToString(CultureInfo.InvariantCulture));
                    sbSentence.Append(",");
                    sbSentence.Append(this.MessageType);
                    sbSentence.Append(",");
                    if (this.RouteNumber > 0)
                        sbSentence.Append(this.RouteNumber);

                    sbSentence.Append(",");

                    if (RouteName.Length > 0)
                    {
                        sbSentence.Append(this.RouteName);
                        sbSentence.Append(",");
                    }
                    //routepoints are in a collection
                    if (RoutePoints != null)
                    {
                        //for (int point = 0; point < RoutePoints.Count; point++)
                        foreach (PmgnRteRoutePoint point in RoutePoints)
                        {
                            if (point.Name.Length > 0)
                            {
                                sbSentence.Append(point.Name);
                                sbSentence.Append(",");
                            }
                        }
                        //remove last comma
                        sbSentence.Remove(sbSentence.Length - 1, 1);

                    }
                    //remove any trailing commas
                    if (sbSentence.ToString().EndsWith(","))
                        sbSentence.Remove(sbSentence.Length - 1, 1);

                    sbSentence.Append("*");

                    //calculate checksum
                    long checksum = CalculateChecksum(sbSentence.ToString());

                    string chkSum = checksum.ToString("X");
                    if (chkSum.Length == 1)
                        sbSentence.Append("0");
                    sbSentence.Append(chkSum);

                    return sbSentence.ToString();
                }
                else
                {
                    //passed in to simply return
                    return m_strSentence;
                }
            }
            catch (Exception ex)
            {
                throw new CreateSentenceException(ex.Message, ex);
            }

        }
        /// <summary>
        /// Some devices that do not support route names place a Waypoint name
        /// where the route name should be. This method corrects this for a route
        /// and returns the route name. In addition the method attempts to identify
        /// icons within the Waypoint names.
        /// </summary>
        /// <remarks>
        /// If there is only one sentence in the array, it cannot be determined easily
        /// whether the route uses a route name.
        /// However several approaches are adopted together and in almost all cases the correct
        /// values are returned.
        /// Problems can occur with Waypoint names of 1 character long or with routename that are
        /// the same as a waypoint name.
        /// </remarks>
        /// <param name="sentences">An array of SentencePmgnRte setntence objects.</param>
        /// <returns>Routename</returns>
        internal static string FixRouteNameAndIcons(ref SentencePmgnRte[] sentences)
        {
            //assume we do not have a route name
            bool noRouteName = true;
            string routeName = "";
            PmgnRteRoutePointCollection routePoints;
            int waypointCount = 0;

            if (sentences != null)
            {

                //as we have some sentences, assume we have a route name
                noRouteName = false;

                //get the first sentences routename
                routeName = sentences[0].RouteName;

                //check with other sentences assuming there are some
                //be aware that there may only be one sentence and it may or may not use route names
                //thankfully magellan insist on an 82 char limit on sentences so this should help
                //prevent this situation
                if (sentences.Length > 1)
                {
                    //loop through sentences looking for a common routename in each
                    for (int f = 1; f < sentences.Length; f++)
                    {
                        switch (sentences[f].MessageType.ToUpper(CultureInfo.InvariantCulture))
                        {
                            case "C":	//Complete

                                //all routename fields should be the same for each sentence
                                //else it is safe to assume that the sentence doesn't include
                                //a routename includes 'C' and 'M' messages
                                if (sentences[f].RouteName != routeName)
                                {
                                    noRouteName = true;
                                    break;
                                }
                                break;
                        }
                    }
                }
                else
                {
                    //only one sentence that may or may not use a routename cannot determine 
                    //this as we cannot try a Waypoints.Find as the route could contain an 
                    //internal database point of interest/city etc. which wouldn't be in the 
                    //Waypoints collection.

                    //all we can do is check to see if icons exist in the points as they
                    //stand if they do then we are either ok or all the waypoints are defined
                    //with a name of 1 character long - we'll assume all is ok otherwise
                    //assume that no routename is used. This would give us an extra waypoint
                    //rather than loosing one. This is a crazy situation for which the magellan
                    //software designer should be shot!

                    if (UsesIcons(sentences[0], noRouteName))
                    {
                        sentences[0].Icons = IconStatus.IconsUsed;
                        noRouteName = false;
                    }
                    else
                    {
                        sentences[0].Icons = IconStatus.IconsNotUsed;
                        noRouteName = true;
                    }
                }

                //if noRoute name is true then it means waypoints must exist in
                //the Route Name position within the sentence. If the route name
                //did not exists but the waypoints were not in the routename position
                //the noRouteName would be false but the routename would be blank.
                //Fix the sentences.
                if (noRouteName)
                {
                    for (int f = 0; f < sentences.Length; f++)
                    {

                        switch (sentences[f].MessageType.ToUpper(CultureInfo.InvariantCulture))
                        {
                            case "C":	//Complete

                                //set its Icon Status
                                if (UsesIcons(sentences[f], noRouteName))
                                {
                                    sentences[f].Icons = IconStatus.IconsUsed;
                                }
                                else
                                {
                                    sentences[f].Icons = IconStatus.IconsNotUsed;

                                }

                                //get number of waypoints, include the waypoint in the routename (i.e. +1)
                                waypointCount = sentences[f].RoutePoints.Count() + 1;

                                //create a new array for the routePoints
                                routePoints = new PmgnRteRoutePointCollection();

                                //set the first routepoint and clear the routename
                                PmgnRteRoutePoint routePoint = new PmgnRteRoutePoint();
                                routePoint.Name = sentences[f].RouteName;
                                routePoints.Add(routePoint);
                                sentences[f].RouteName = "";

                                //copy the rest

                                //for (int point = 0; point < sentences[f].RoutePoints.Length; point++)
                                foreach (PmgnRteRoutePoint point in sentences[f].RoutePoints)
                                {
                                    routePoints.Add(point);

                                }
                                //now we have a corrected collection, assign it to the sentence
                                sentences[f].RoutePoints = routePoints;

                                break;
                        }

                        //
                        //

                    }
                    //no routename so set to blank
                    routeName = "";
                }

                ////now determine if icons are used by the following method
                ////this could have already been done if we have only one sentence
                ////but do it anyway
                //SetIconStatus(ref sentences);
            }
            return routeName;
        }
        /// <summary>
        /// Rreturns true if icons are used and false otherwise.
        /// </summary>
        /// <param name="sentence">An array of SentencePmgnRte sentence objects.</param>
        /// <param name="noRouteNameExists"></param>
        /// <returns></returns>
        private static bool UsesIcons(SentencePmgnRte sentence, bool noRouteNameExists)
        {
            bool noIcons = true;


            if (sentence != null)
            {

                switch (sentence.MessageType.ToUpper(CultureInfo.InvariantCulture))
                {
                    case "C":	//Complete sentences only

                        //now determine if icons are used by the following method
                        //if no routename exists then it is faily simple:
                        //1		even number of routepoints i.e. name followed by icon
                        //2		odd index  e.g. routepoint 1,3,5 etc. should contain a single character
                        if (noRouteNameExists)
                        {
                            //check for even number of routepoints
                            if (sentence.RoutePoints.Count() % 2 == 0)
                            {
                                //assume icons must be used
                                noIcons = false;

                                for (int point = 1; point < sentence.RoutePoints.Count(); point = point + 2)
                                {

                                    if (sentence.RoutePoints[point].Name.Length != 1)
                                    {
                                        //icons not used so set property
                                        noIcons = true;

                                        //icons not used so break out of inner points loop
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                //icons cant be used so set Icons property
                                noIcons = true;
                            }
                        }
                        else
                        {
                            //if routename exists then it is faily simple:
                            //1		odd number of routepoints i.e. name followed by icon
                            //2		odd index  e.g. routepoint 1,3,5 etc. should contain a single character

                            //this needs implementing as and when we find a device that uses routenames.
                        }
                        break;
                }

            }
            //return true if iconsd are used
            return !noIcons;
        }
    }
}
